#pragma once

#include <iostream>
#include <string>
#include <ctime>
#include <memory>
#include <unistd.h>
#include <jsoncpp/json/json.h>

const std::string psep = "\r\n";

// {} -> len\r\n{}\r\n
std::string Encode(std::string package)
{
    std::string ret = "";
    std::string len_str = std::to_string(package.size());

    ret += len_str;
    ret += psep;
    ret += package;
    ret += psep;

    return ret;
}

// len\r\n
// len\r\n{}\r\nlen\r
std::string Decode(std::string &package)
{
    size_t pos = package.find(psep);
    if (pos == std::string::npos)
        return "";

    std::string len_str = package.substr(0, pos);
    if (len_str.size() == 0)
        return "";

    size_t psep_size = psep.size();
    size_t len_size = len_str.size();
    size_t text_size = std::stoi(len_str);
    if (package.size() < len_size + 2 * psep_size + text_size)
        return "";

    std::string text = package.substr(len_size + psep_size, text_size);

    package.erase(0, len_size + 2 * psep_size + text_size);

    return text;
}

class Request
{
public:
    Request(){}
    Request(int x, int y, char op)
        :_x(x), _y(y), _op(op)
    {}

    std::string serialize()
    {   
        Json::Value root;
        root["x"] = _x;
        root["y"] = _y;
        root["op"] = _op;

        Json::FastWriter writer;

        return writer.write(root);
    }

    bool deserialize(std::string& in)
    {
        // std::cout << in;
        Json::Value root;
        Json::Reader reader;
        bool res = reader.parse(in, root);
        if(!res)
            return false;
        
        _x = root["x"].asInt();
        _y = root["y"].asInt();
        _op = root["op"].asInt();

        return true;
    }

    void print()
    {
        std::cout << "_x: " << _x << ", _y: " << _y << ", _op: " << _op << std::endl;
    }


    int _x;
    int _y;
    char _op;
};

class Response
{
public:
    Response() {}

    Response(int res, int code)
        : _res(res), _code(code)
    {
    }

    std::string serialize()
    {
        Json::Value root;
        root["res"] = _res;
        root["code"] = _code;

        Json::StyledWriter writer;

        return writer.write(root);
    }

    bool deserialize(std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        bool res = reader.parse(in, root);
        if (!res)
            return false;

        _res = root["res"].asInt();
        _code = root["code"].asInt();

        return true;
    }

    void print()
    {
        std::cout << "_res: " << _res << ", _code: " << _code << std::endl;
    }
    int _res;
    int _code;
};

class Factory
{
public:
    std::shared_ptr<Request> BuildRequest()
    {
        int x = rand() % 100;
        int y = rand() % 100;

        int i = rand() % 4;
        std::string ops = "+-*/";

        return std::make_shared<Request>(x, y, ops[i]);
    }

private:
};